Part 55: Assignment #26: Security Nightmare
Security NightmareSee? Told you Joe was more sober. But... a door lock based off the national ID card? How's that supposed to work?
Oh. That's how it's supposed to work.
I can see so many potential flaws with this.
1. A proper design for a lock like this would store the card number (which I assume is the user's actual ID card#). Presumably, this would make it possible to read the last visitor's identity straight out of the door! Privacy concerns, anyone?
2A. If this were someone else designing this, I can imagine them being tempted to cut costs - and, instead of storing the ID number, storing a digest or hash of it. (Something that fits in a register as opposed to having to use a memory chip.) The problem is, even with a hash algorithm that can output a number in the full range of -999 to 999, that's 1999 possible hashes for 10^10 (10 billion) possible ID card numbers. Which sounds like a large enough possibility space that we wouldn't have to worry about one person's card opening another's door, but...
2B. I don't know how China (or Japan) generates their ID card numbers, but it's possible it's something like a numerical counter [00001, 00002, 00003] or a numerical counter but with a check digit at the end (0001/7, 0002/4, 0003/1). If this is the case, the numbers would naturally tend to have lots of zeros in the high digits. This would reduce the space of ID numbers you'd actually run into by a large amount, and (unless the hash algorithm were really carefully designed) increase the chance of two peoples' keys opening the same door by a lot.
2C. If this is for the Chinese market (1.5+ billion people) , as opposed to the Japanese market (120+ million), then there's the second issue that a large amount of the ID-card-number space would be in use by people. So that increases the chance of two peoples' keys opening the same door by the people by sheer weight of numbers.
2D. It gets worse if the master key is also compared by the same hashing algorithm. Some lucky person could come along whose card gets read as the master key. The problems with that set of circumstances is obvious.
2E. And even if all these objections were addressed somehow, you shouldn't cut corners on security. It's just morally wrong to do so.
3. Like Carl said, if someone copies the master key, it's all over.
I did some google searching and looked into how more, uh, higher-end hotels do it. Middle-end hotels tend to have the card programmed with the room number and date of stay; it's still a concern if someone filches the master key, but as the master key isn't needed to initially open the door, the hotel can (theoretically) keep it under tighter guard.
This type of system does still appear to be susceptible to someone reprogramming their card if they're carrying around a portable card writer, either to extend the date of stay, change the room number, or just turning their card into a master key. (And yes, I am assuming that someone might be trying to bust into my door lock with a card writer.)
Really high-end hotels do things like my college's engineering building did it. Everyone's key card says who they are and that's it. When someone tries to use the key card on a lock, their identity gets transmitted to the central door server, which checks whether X person is allowed to be where they say they are (and logs it) and then opens the door. While this doesn't preclude against someone gaining privileged access entirely, it does allow variable levels of privileged access as well as figuring out whose card got filched in the event a breach does occur - and there's no one guaranteed master key pattern that an adversary can try.
(The one downside to this is that if the door server crashes, you better break out the physical master key to the server room. This happened at my college one time...)
Anyway. Back to work.
The design I'm going with is definitely going to need a memory chip, to store the user's card number. Effectively, there's two separate operations the lock has to do when triggered by the card reader: write the new card (if learn is on), or test vs the stored card (otherwise). Testing and writing/reading feel like they should take separate chips - by which I mean I think they won't fit on one.
Here's my initial (¥12 / 671 avg power) design:
The main (lower) MC is responsible for handling input from the card reader. If learn is on, it writes the 10 pieces of input directly into the memory chip. Otherwise, it passes them to the upper MC, which tests each digit of the input to see if it's a 9, or matching the input. (It uses separate digits in its acc to mark which conditions are still OK so far.) Once everything is read, it sends a secret value (-972) to the upper (tester) MC to say that the read is complete.
When the tester MC gets the -972, it sends the value of its acc to the main MC. The value will be 100 (in case of master key) or 10 (in case of user key) if the lock is to be opened, and 0 otherwise. Depending on the value, and if learn is not on, the main MC opens the lock.
I know that I usually try to optimize jobs more once Ive got them working, but I dont think Im going to really take a big stab at it this time around. This ones working, it seems relatively hardened, and if I tried to move things around and lower cost or power, I'd risk introducing security holes into the design.
So: time to send this off.
Well, excuse you, Carl.
Unfortunately, there are two problems with this. Problem number one is that there's a certain amount of redundancy that doesn't need to be there - the sending of -972 to the tester MC happens even when the lock was learning a new code, which isn't necessary at all.
Problem number two - well, Carl caught this one, actually (thus his reaction). He pointed out: If a card with the secret 'read complete' value gets inserted into the slot, the tester will try to send its acc to the main MC even as the main MC is waiting for more input from the card reader, and the lock will break and need to be rebooted.
I'm going to see if I can eliminate these issues from the design. I might have to make the lock reject negative-number input from the card reader. We'll have to see...
[ ~ ]
The solution turned out to be a lot simpler than I thought. I didn't need to keep track of the number of values read in, and I didn't need to send a secret value to the tester MC. All I need to do is start any card-number write (or read) at the first cell of the memory chip, and test if we've gotten through 10 cells so far by querying the memory chip's address line.
The result looks like this.
Without the business with setting the main MC's acc to 10, and the secret value, I've saved several lines of code. It still costs the same (both MCs have too many pins in use to be converted to MC4000s, and the upper MC has too many lines of code in use as well), but power usage has gone down to 563 units on average.
The bottom MC redirects input values (to the memory chip or tester) as appropriate. (The nop is there before its test so that it can wait for the tester to read the value before it checks the address - this ensures it doesn't go too fast and end up trying to read an extra value.) That's all it really does.
The upper MC runs the tests 10 times (once on each digit of the code, as passed by the lower MC), and if either of its checks are still OK at the end of it, it will send the unlock signal.
This design is a good next step, but now that I am in an optimizing frame of mind I can see room for more improvement (that is, improvement that still keeps everything secure). The lower MC could easily be converted to a MC4000X if the tester MC was the one reading learn. Of course, that would imply that the tester MC would be the one writing to the memory chip... maybe we might not need the lower MC at all? I'll keep playing with it and see how far I can take it.
[ ~ ~ ]
I tried to make it fit into one single MC6000. The problem is, once you've got the lines for:
waiting for input and moving it into a register (2)
testing learn and writing the input if it's on (2)
testing the input (4)
checking the address line to see if you're far enough, and jumping back if not (2)
testing if either check (in acc) is still OK, that learn is off, and then sending to unlock if so for both (3)
and resetting the pointer on the memory chip and the acc (2)
...you're up to 15 lines - one over what'll fit.
The above design is my answer to that. It gets rid of the 'move the input into a register' line by using a MC4000 as a cache, which sends out each read value twice. From there, everything fits in 14 lines in the big MC.
This design's stats are a little better cost-wise, but power usage is back up (¥10, 650 average power). More importantly, I'm not that happy with it. There has to be a chance that there's some way to save a different line from the big MC. Maybe I can stop it from testing learn twice? That'd require the condition at the end be massaged so that it always fails when learn is on, but I'm not sure that's something I can do and still have it work properly / be secure under normal use...
I'll think about it some and see if this is the end of the line or not.
[ ~ ~ ~ ]
Here it is.
I consider this to be a masterpiece. It's efficient (446 average power), inexpensive (¥7), and still secure against everything except a keycard with the wrong length data (which I can't do anything about because the card reader input is blocking) or copying the master key (which I can't do anything about, period).
I was able to fit it all in 15 lines in 14 by combining the "check that the conditions are OK and learn is off" line into one. Here's how it works.
At the start, the MC sets its acc to 11. (Previously it was 110, but the change is required to make this all work out.) The tens digit being 1 means that it could be the master key; the ones digit being 0 means that it could be the user key. During testing, these digits are switched off (set to 0) as it becomes clear that it can't be a specific key - this means that the resulting value will be 11, 10, or 1 (if the key is good) or 0 (if not).
Then we get to the unlock condition: tgt acc p0. p0 is the learn input, and it's 100 when learn is on, and 0 otherwise.
So, if learn is on, acc will be anywhere from 0 to 11, which will not be greater than 100, so the lock will never open.
If learn is off, though, acc will be compared with 0 - and the condition will fire if acc is greater than 0, which would mean it's a valid key. And so the lock opens if the key is valid.
SleepCUBES may be in the mood for cutting corners, but I can at least try and make sure as few have been cut as possible. Sleep well, y'all tired-out salarymen. I salute you.
P.S: I've even managed to get in some more work on my secret project recently! It's coming along nicely: